//========================================================//
// Point to Relay Protocol Classes                        //
// Author: Krechetov Anton aka kae   kae@gorodok.net      //
// NSU FIT September 2004                                 //
//========================================================//

#ifndef _POINT_TO_RELAY_PROTOCOL_
#define _POINT_TO_RELAY_PROTOCOL_

#include "../osocklib/sockstack.h"
#include "../icrypto/icrypto.h"
#include <queue>
#include <map>
#include <string>
#include "../osocklib/osocklib.h"
//#include "C:\\Documents and Settings\\sfaktorovich\\My Documents\\remoteconsole2\\vld.h"

namespace BAM
{
	class cPrpPacket;
	class cPrpJob;
	class cPrpManager;
	class cPrpController;
	class cPrpSocket;

	//$added
	struct logControl
	{
		cPrpController* control;
		cPacket login;
		dword dest;
	};

	typedef sdword PrpState;
	typedef dword PrpField;
	typedef byte PrpPacketType;
	typedef byte PrpRetCode;
	typedef byte PrpSocketType;

	static const PrpSocketType Prp_Socket_Relay = 0;
	static const PrpSocketType Prp_Socket_Client = 1;
	static const PrpSocketType Prp_Socket_Server = 2;

	static const PrpPacketType Prp_Packet_Connect = 0x80;
	static const PrpPacketType Prp_Packet_ConnAcc = 0x82;
	static const PrpPacketType Prp_Packet_Restore = 0x81;
	static const PrpPacketType Prp_Packet_RestAcc = 0x83;
	static const PrpPacketType Prp_Packet_Alive = 0x84;
	static const PrpPacketType Prp_Packet_Alive_Response = 0x85;

	static const PrpPacketType Prp_Packet_PartyGone = 0x86;
	//      static const PrpPacketType Prp_Packet_BadPacket = 0x87;
	//      static const PrpPacketType Prp_Packet_Message = 0x88;
	static const PrpPacketType Prp_Packet_Reset = 0x8A;
	static const PrpPacketType Prp_Packet_Data = 0xFF;

	static const PrpState Prp_State_Error = 0;
	static const PrpState Prp_State_BadPacket = 1;
	static const PrpState Prp_State_None = 2;
	static const PrpState Prp_State_Initializing = 3;
	static const PrpState Prp_State_Connecting = 4;//TODO add state_reporting to use when sending ConnAcc packet with non-zero ret field
	static const PrpState Prp_State_Resetting = 5;
	static const PrpState Prp_State_Restoring = 6;
	static const PrpState Prp_State_Normal = 7;
	static const PrpState Prp_State_Finished = 8;
	static const PrpState Prp_State_Waiting_For_Alive_Response = 9;

	static const string Prp_Debug_Str[] = {
							string("Prp_State_Error"), 
							string("Prp_State_Bad_Packet"), 
							string("Prp_State_None"),
							string("Prp_State_Initializing"),
							string("Prp_State_Connecting"), 
							string("Prp_State_Resetting"),
							string("Prp_State_Restoring"),
							string("Prp_State_Normal"),
							string("Prp_State_Finished")
							};

	static const PrpField Prp_Field_Type = 0;
	static const PrpField Prp_Field_OutCounter = 1;
	static const PrpField Prp_Field_InCounter = 2;
	static const PrpField Prp_Field_Length = 3;
	static const PrpField Prp_Field_Reserved = 4;
	static const PrpField Prp_Field_Data = 0;
	static const PrpField Prp_Field_Sign = 1;

	static const PrpRetCode Prp_Ret_Normal = 0x00;
	static const PrpRetCode Prp_Ret_BadSign = 0x01;
	static const PrpRetCode Prp_Ret_BadLogin = 0x02;
	static const PrpRetCode Prp_Ret_RndRepeated = 0x03;
	static const PrpRetCode Prp_Ret_ChangePass = 0x04;
	static const PrpRetCode Prp_Ret_Origin = 0x05;
	static const PrpRetCode Prp_Ret_NoDest = 0x06;
	static const PrpRetCode Prp_Ret_DestOff = 0x07;
	static const PrpRetCode Prp_Ret_ServError = 0x08;
	static const PrpRetCode Prp_Ret_BadPacket = 0x09;

	static const dword Prp_Count_Bin = 2;
	static const dword Prp_Count_Int = 5;

	//static const dword Prp_Time1 = 30;//in seconds
	static const dword Prp_Time2 = 10;
	//static const dword Prp_Time3 = 90;
	static const dword Prp_Time1 = 5;//in seconds
//	static const dword Prp_Time2 = 11000;
	static const dword Prp_Time3 = 30;  //was 15

	static const dword Prp_Len_Counter = 2;
	static const dword Prp_Len_Dh = 128;
	static const dword Prp_Len_Padding = 8;
	static const dword Prp_Len_Rnd = 4;
	static const dword Prp_Len_Id = 4;
	static const dword Prp_Len_Reserved = 1;
	static const dword Prp_Len_Ret = 1;
	static const dword Prp_Len_ConnId = 3;
	static const dword Prp_Len_Null = 4;
	static const dword Prp_Len_Login = 12;
	static const dword Prp_Len_Hash = 16;
	static const dword Prp_Len_Header = 8;
	static const dword Prp_Len_Sign = Prp_Len_Hash;
	static const dword Prp_Min_OriginId = 0x80000001;
	static const dword Byte256 = 0xFF;
	static const dword Byte64k = 0xFFFF;
	static const dword Byte16M = 0xFFFFFF;

	static const SocketException EXC_LoginSize("Incorrect login size");
	static const SocketException EXC_RegisterController("Too many connections");
	static const SocketException EXC_NoId("No such connection id");
	//      static const PrpException EXC_PrpHash("Packet sign is invalid");
	//      static const PrpException EXC_PrpSize("Packets longer than 65527b are not supported");

	class cPrpPacket: public iPacketWrapper, public cPacket
	{
		cHMAC_MD5Transform *vSign;
		bool vSign_set;

	public:
		cPrpPacket();
		cPrpPacket(cHMAC_MD5Transform &Sign);
		cPrpPacket(const cPrpPacket &P);
		cPrpPacket(const cPacket &P, cHMAC_MD5Transform &Sign);
		cPrpPacket(const cPacket &P);
		//              cPrpPacket operator=(const cPrpPacket p);
		qword getIntField(dword Index) const;
		void setIntField(dword Index, qword Val);
		dword getIntFieldCount() const;

		cPacket getBinField(dword Index) const;
		dword getBinFieldLength(dword Index) const;
		void setBinField(dword Index, const cPacket &P);
		dword getBinFieldCount() const;

		dword getHeaderLength() const;
		dword getFullLength(const cPacket &P) const;
		dword getMaxLength() const;

		cPacket compile();
		iPacketWrapper* decompile(const cPacket &P);
	};

	class cPrpManager
	{
	private:
		map<dword, cPrpController*> vControlMap;
		//$Map, which keep DestIP of servers
		map<dword, logControl> vDestControl;
		dword vConnIds;
		//$added count vDestControl
		dword vConnDest;
		cMutex vMapLock;

		cPrpManager();

	public:
		~cPrpManager();
		static cPrpManager& getInstance();
		dword getOriginId();//TODO: generate id's in another way - kae
		dword registerController(cPrpController* ctrl);
		void unregisterController(dword connid);
		//m
		bool findControl(dword dest, cPacket login,bool who);
		//$added addLC, removeLC and findServer;
		bool addLogControl(logControl lc);
		void removeLogControl(cPrpController* pc);
		bool findServer(dword dest, cPacket login);
		bool transferReconnect(dword connid, iSocket* transp, const cPrpPacket p);
	};

	class cPrpController : public cActiveController, virtual public cAssemblerController
	{
	private:
		cPacket vInitLogin;

		PrpState vState;
		
		
		dword vRnd;
		word vICounter;
		word vOCounter;
		queue<cPacket> vIncomingQ;
		queue<cPacket> vOutcomingQ;
		queue< pair<word, cPacket> > vSaveQ;
		queue< pair<iSocket*, const cPrpPacket> > vRestoreQ;
		cMutex vRestoreLock;
		bool vRegistered;
		cLoginManager* vLoginManager;
		cDHManager* vDH;
		cHMAC_MD5Transform vSign;

		void outPacket(cPrpPacket &P);
		void resetKey(const cPacket &Agree);
		void tellIds();
		void regControl();
		void unregControl();
		void sendReconnect();
		void tryAnswerReconnect();
		void finishReconnect(word oldinc);
		cPrpPacket FormConnectPacket();
		cPrpPacket FormConnAccPacket(byte ret);
		cPrpPacket FormRestorePacket();
		cPrpPacket FormRestAccPacket(byte ret);
		byte ProcessConnectPacket(const iPacketWrapper& p);
		byte ProcessConnAccPacket(const cPacket& p);
		pair<word, cPacket> ProcessRestorePacket(const iPacketWrapper& p);
		pair<byte, word> ProcessRestAccPacket(const iPacketWrapper& p);
		void RecvResetPacket(const iPacketWrapper& P);
		void RecvConnectPacket(const iPacketWrapper& P);
		void RecvConnAccPacket(const cPacket& Data);
		void RecvRestorePacket(const iPacketWrapper& P);
		void RecvRestAccPacket(const iPacketWrapper& P);
		void RecvHelloPacket(const iPacketWrapper& P);

	protected:
		void received(const iPacketWrapper &P);

	public:


		PrpSocketType vType;

		dword vOrigin, vDestination, vConnId;

		cPrpManager& vManager;

		cTimeout vTimeout;
		cTimeout vAliveTimeout;	
		cPacket vLogin;

		void setState(PrpState st);

		/*****************************************************/

		cPrpController(cLoginManager* lm, cPacket login, word id, PrpSocketType type);
		~cPrpController();
		void onConnect(const cIPAddr &Addr);
		void onDisconnect();
		void onInit(const cIPAddr &Addr);
		void onSend(const cPacket &P);
		void onRecv(const cPacket &P);
		bool haveOutcome() const;
		bool haveIncome() const;
		bool isInited() const;
		bool isFinished() const;
		Sock_State state(Sock_State TransportState) const;
		cPacket outcome();
		cPacket income();
		void onIdle(Sock_State TransportState);
		//m
		bool CtrlCmp(dword dest, cPacket login,bool who);
		//m
		void sendPartyGonePacket();
		void addRestorer(iSocket* transp, const cPrpPacket p);
		iPacketController* clone() const;
		bool isStateOk()
		{
			return ( (this->vState == Prp_State_Normal) 
				|| (vState == Prp_State_Waiting_For_Alive_Response) 
				);
		}
		PrpState getState();
	};

	class cPrpSocket : public cActiveSocket
	{
	public:
		cPrpSocket(iSocket* trnsp, cLoginManager *lm,
			const cPacket &login, word id, PrpSocketType type);
		virtual cPacket recv();

		bool RecvEx(cPacket *ret, bool block = false);

		
	};

}
#endif

